﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;
using System.IO;
using System.Net;
using Microsoft.Win32;
using org.googlecoders.GetLatestChromium.Properties;
using System.Xml.Serialization;
using System.Windows.Forms;

namespace org.googlecoders.GetLatestChromium
{
	public static class ChromiumUpdate
	{
		#region Update
		public static void UpdateThread(System.ComponentModel.BackgroundWorker worker)
		{
			Settings settings = new Settings();
			bool hasShownInstallError = false;
			while (!worker.CancellationPending)
			{
				settings.Reload();
				if (settings.NextCheck < DateTime.Now)
				{
					worker.ReportProgress(0,"Checking for updates...");
					settings.LastCheck = DateTime.Now;
					if (CheckForUpdate(settings))
					{
						worker.ReportProgress((int)System.Windows.Forms.ToolTipIcon.Info, "Downloading update...");
						Update(settings);
						worker.ReportProgress(0, "Downloaded update.");
						Changelog cl = GetChangeLog(settings.Location, settings.TheirBuild);
						try
						{
							worker.ReportProgress(99,cl.Logs[0].LogEntries[0].Message);
						}
						catch(Exception ex)
						{
						}
					}
					else
					{
						worker.ReportProgress(0, "No new updates.");						
					}
					settings.NextCheck = DateTime.Now.AddHours(settings.Interval);
					settings.Save();
				}
				else
				{
					worker.ReportProgress(0, "Next check: " + settings.NextCheck.ToShortDateString() + " " + settings.NextCheck.ToShortTimeString());
				}
				
				if (!string.IsNullOrEmpty(settings.InstallerPath))
				{
					if (IsChromiumRunning)
					{
						if(!hasShownInstallError)
						{
							worker.ReportProgress((int)System.Windows.Forms.ToolTipIcon.Warning, "Update will install when Chromium is closed.");
							hasShownInstallError = true;
						}
					}
					else if (InstallExe(settings.InstallerPath))
					{
						worker.ReportProgress((int)System.Windows.Forms.ToolTipIcon.Info, "Installing update...");
						settings.InstallerPath = null;
						settings.OurBuild = settings.TheirBuild;
						settings.OurRevision = settings.TheirRevision;
						settings.Save();
						worker.ReportProgress((int)System.Windows.Forms.ToolTipIcon.Info, "Installed build " + settings.OurBuild);
						hasShownInstallError = false;
					}
					else
					{
						worker.ReportProgress((int)System.Windows.Forms.ToolTipIcon.Error, "Install failed.");
					}
				}
				Thread.Sleep(new TimeSpan(0, 0, 5));
			}
		}

		public static int GetLastestBuild(BuildLocation location)
		{
			int ret = 0;
			try
			{
				HttpWebRequest req = (HttpWebRequest)WebRequest.Create(location.Url + location.Latest);
				req.CachePolicy = new System.Net.Cache.HttpRequestCachePolicy(System.Net.Cache.HttpRequestCacheLevel.Reload);
				HttpWebResponse res = (HttpWebResponse)req.GetResponse();
				if (res.StatusCode == HttpStatusCode.OK)
				{
					using (StreamReader reader = new StreamReader(res.GetResponseStream()))
					{
						if (!int.TryParse(reader.ReadToEnd(), out ret))
							ret = 0;
						reader.Close();
					}
				}
				res.Close();
			}
			catch (Exception ex)
			{
			}

			return ret;
		}

		public static int GetRevision(BuildLocation location, int build)
		{
			int ret = 0;
			try
			{
				if (build > 0)
				{
					HttpWebRequest req = (HttpWebRequest)WebRequest.Create(location.Url + build + "/" + location.Revisions);
					req.CachePolicy = new System.Net.Cache.HttpRequestCachePolicy(System.Net.Cache.HttpRequestCacheLevel.Reload);
					HttpWebResponse res = (HttpWebResponse)req.GetResponse();
					if (res.StatusCode == HttpStatusCode.OK)
					{
						using (StreamReader reader = new StreamReader(res.GetResponseStream()))
						{
							string json = reader.ReadToEnd();
							string[] props = json.Replace("{", "").Replace("}", "").Split(',', ':');
							for (int indx = 0; indx < props.Length; indx += 2)
							{
								if (props[indx] == "\"chromium_revision\"")
								{
									if (!int.TryParse(props[indx + 1], out ret))
										ret = 0;
									break;
								}
							}
							reader.Close();
						}
					}
					res.Close();
				}
			}
			catch (Exception ex)
			{
			}

			return ret;
		}

		public static Changelog GetChangeLog(BuildLocation location, int build)
		{
			Changelog ret = null;
			try
			{
				if (build > 0)
				{
					HttpWebRequest req = (HttpWebRequest)WebRequest.Create(location.Url + build + "/" + location.ChangeLog);
					req.CachePolicy = new System.Net.Cache.HttpRequestCachePolicy(System.Net.Cache.HttpRequestCacheLevel.Reload);
					HttpWebResponse res = (HttpWebResponse)req.GetResponse();
					if (res.StatusCode == HttpStatusCode.OK)
					{
						XmlSerializer serializer = new XmlSerializer(typeof(Changelog));
						ret = (Changelog)serializer.Deserialize(res.GetResponseStream());
					}
					res.Close();
				}
			}
			catch (Exception ex)
			{
			}

			return ret;
		}

		public static bool CheckForUpdate(Settings settings)
		{
			settings.TheirBuild = GetLastestBuild(settings.Location);
			settings.TheirRevision = GetRevision(settings.Location, settings.TheirBuild);
			settings.Save();

			if (settings.TheirRevision > settings.OurRevision)
				return true;
			else
				return false;
		}

		public static void Update(Settings settings)
		{
			string installerpath = DownloadExe(settings.Location, settings.TheirBuild);

			if (!string.IsNullOrEmpty(installerpath))
			{
				settings.InstallerPath = installerpath;
				settings.Save();
			}
		}

		public static string DownloadExe(BuildLocation location, int build)
		{
			try
			{
				HttpWebRequest req = (HttpWebRequest)WebRequest.Create(location.Url + build.ToString() + "/" + location.Exe);
				req.CachePolicy = new System.Net.Cache.HttpRequestCachePolicy(System.Net.Cache.HttpRequestCacheLevel.Reload);
				HttpWebResponse res = (HttpWebResponse)req.GetResponse();
				if (res.StatusCode == HttpStatusCode.OK)
				{
					string filepath = Path.GetTempFileName() + ".exe";
					using (FileStream stream = new FileStream(filepath, FileMode.Create))
					{
						using (BinaryWriter writer = new BinaryWriter(stream))
						{
							using (BinaryReader reader = new BinaryReader(res.GetResponseStream()))
							{
								byte[] buf = new byte[1024];
								int cnt;
								while ((cnt = reader.Read(buf, 0, 1024)) > 0)
								{
									writer.Write(buf, 0, cnt);
								}
								reader.Close();
							}
							writer.Close();
						}
						stream.Close();
					}
					return filepath;
				}
			}
			catch (Exception ex)
			{
			}

			return null;
		}

		public static bool IsChromiumRunning
		{
			get
			{
				foreach (Process process in Process.GetProcesses())
				{

					if (process.ProcessName.Equals("chrome", StringComparison.InvariantCultureIgnoreCase))
						return true;
				}
				return false;
			}
		}
		
		public static string ChromiumVersion
		{
			get
			{
				return (string)Registry.GetValue("HKEY_CURRENT_USER\\Software\\Chromium","pv", "0.0.0.0");
			}
		}

		public static bool InstallExe(string filepath)
		{
			if (IsChromiumRunning)
				return false;

			try
			{
				Process process = new Process
				{
					StartInfo = { FileName = filepath, WindowStyle = ProcessWindowStyle.Hidden }
				};
				process.Start();
				process.WaitForExit();
				process.Close();
				return true;
			}
			catch (Exception ex)
			{
			}
			return false;
		}
		#endregion Update

	}
}
